﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Bigeagle.WPF.Controls
{
    /// <summary>
    /// 坐标系转换类
    /// </summary>
    public class CoordinateTransform
    {
        public static int TileSize = 256;
        private static double initialResolution = 2 * Math.PI * 6378137 / TileSize;
        private static double originShift = 2 * Math.PI * 6378137 / 2.0;
        // private static double maxLon = 85.05113;

        /// <summary>
        ///  Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:900913
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        /// <param name="mx"></param>
        /// <param name="my"></param>

        public static void LatLonToMeters(double lat, double lon, out double mx, out double my)
        {
            mx = lon * originShift / 180.0;
            my = Math.Log(Math.Tan((90 + lat) * Math.PI / 360.0)) / (Math.PI / 180.0);

            my = my * originShift / 180.0;
        }
        /// <summary>
        ///Converts XY point from Spherical Mercator EPSG:900913 to lat/lon in WGS84 Datum 
        /// </summary>
        /// <param name="mx"></param>
        /// <param name="my"></param>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        public static void MetersToLatLon(double mx, double my, out double lat, out double lon)
        {
            lon = (mx / originShift) * 180;
            lat = (my / originShift) * 180.0;

            lat = 180 / Math.PI * (2 * Math.Atan(Math.Exp(lat * Math.PI / 180.0)) - Math.PI / 2.0);
        }

        /// <summary>
        /// Converts pixel coordinates in given zoom level of pyramid to EPSG:900913
        /// </summary>
        /// <param name="px"></param>
        /// <param name="py"></param>
        /// <param name="zoom"></param>
        /// <param name="mx"></param>
        /// <param name="my"></param>
        public static void PixelsToMeters(double px, double py, int zoom, out double mx, out double my)
        {
            double res = Resolution(zoom);
            mx = px * res - originShift;
            my = py * res - originShift;
            //    return mx, my
        }

        /// <summary>
        /// 从经纬度坐标字符串到经纬度
        /// </summary>
        /// <param name="geocode"></param>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        /// <returns></returns>
        public static bool GeocodeToLatLon(string geocode, out double lat, out  double lon)
        {
            string[] temp = geocode.Split(',');
            if (double.TryParse(temp[0], out lat) && double.TryParse(temp[1], out lon))
            {
                return true;
            }
            else
            {
                lat = 0;
                lon = 0;
                return false;
            }
        }

        /// <summary>
        /// Converts EPSG:900913 to pyramid pixel coordinates in given zoom level
        /// </summary>
        /// <param name="mx"></param>
        /// <param name="my"></param>
        /// <param name="zoom"></param>
        public static void MetersToPixels(double mx, double my, int zoom, out double px, out double py)
        {
            double res = Resolution(zoom);
            px = (mx + originShift) / res;
            py = (my + originShift) / res;
            //    return px, py
        }

        /// <summary>
        /// 像素坐标系转换到经纬度坐标系
        /// </summary>
        /// <param name="px"></param>
        /// <param name="py"></param>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        public static void PixelsToLatLon(double px, double py, int zoom, out double lat, out double lon)
        {
            double mx, my;
            PixelsToMeters(px, py, zoom, out mx, out my);
            MetersToLatLon(mx, my, out lat, out lon);
        }

        /// <summary>
        /// 经纬度坐标系到像素坐标系
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        /// <param name="zoom"></param>
        /// <param name="px"></param>
        /// <param name="py"></param>
        public static void LatLonToPixels(double lat, double lon, int zoom, out double px, out double py)
        {
            double mx, my;
            LatLonToMeters(lat, lon, out mx, out my);
            MetersToPixels(mx, my, zoom, out px, out py);
        }

        /// <summary>
        /// Returns a tile covering region in given pixel coordinates
        /// </summary>
        /// <param name="px"></param>
        /// <param name="py"></param>
        /// <param name="tx"></param>
        /// <param name="ty"></param>
        public static void PixelsToTile(double px, double py, out int tx, out int ty)
        {
            tx = (int)Math.Ceiling(px / TileSize) - 1;
            ty = (int)Math.Ceiling(py / TileSize) - 1;
        }//end method

        /// <summary>
        /// Move the origin of pixel coordinates to top-left corner
        /// </summary>
        /// <param name="px"></param>
        /// <param name="py"></param>
        /// <param name="zoom"></param>
        /// <param name="rx"></param>
        /// <param name="ry"></param>
        public static void PixelsToRaster(double px, double py, int zoom, out double rx, out double ry)
        {
            int mapSize = TileSize << zoom;
            rx = px;
            ry = mapSize - py;
            //    return px, mapSize - py
        }
        //def MetersToTile(self, mx, my, zoom):
        //    "Returns tile for given mercator coordinates"

        //    px, py = self.MetersToPixels( mx, my, zoom)
        //    return self.PixelsToTile( px, py)

        /// <summary>
        /// Returns bounds of the given tile in EPSG:900913 coordinates
        /// </summary>
        /// <param name="tx"></param>
        /// <param name="ty"></param>
        /// <param name="zoom"></param>
        /// <param name="rectangle"></param>
        public static void TileBounds(double tx, double ty, int zoom, out double minx, out double miny, out double maxx, out double maxy)
        {
            PixelsToMeters(tx * TileSize, ty * TileSize, zoom, out minx, out miny);
            PixelsToMeters((tx + 1) * TileSize, (ty + 1) * TileSize, zoom, out maxx, out maxy);
            //    maxx, maxy = self.PixelsToMeters( (tx+1)*self.tileSize, (ty+1)*self.tileSize, zoom )
            //    return ( minx, miny, maxx, maxy )
        }
        //public static void TileLatLonBounds(int tx, int ty, int zoom 
        //    , out double minLat , out double minLon , out double maxLat , out double maxLon )
        //{
        ////    "Returns bounds of the given tile in latutude/longitude using WGS84 datum"

        ////    bounds = self.TileBounds( tx, ty, zoom)
        ////    minLat, minLon = self.MetersToLatLon(bounds[0], bounds[1])
        ////    maxLat, maxLon = self.MetersToLatLon(bounds[2], bounds[3])

        ////    return ( minLat, minLon, maxLat, maxLon )

        //}
        /// <summary>
        /// Resolution (meters/pixel) for given zoom level (measured at Equator)
        /// </summary>
        /// <param name="zoom"></param>
        /// <returns></returns>
        public static double Resolution(int zoom)
        {
            //return (2 * math.pi * 6378137) / (self.tileSize * 2**zoom)
            return initialResolution / (Math.Pow(2, zoom));
        }
        //def ZoomForPixelSize(self, pixelSize ):
        //    "Maximal scaledown zoom of the pyramid closest to the pixelSize."

        //    for i in range(30):
        //        if pixelSize > self.Resolution(i):
        //            return i-1 if i!=0 else 0 # We don't want to scale up

        /// <summary>
        /// Converts TMS tile coordinates to Google Tile coordinates
        /// </summary>
        /// <param name="tx"></param>
        /// <param name="ty"></param>
        /// <param name="zoom"></param>
        /// <param name="gx"></param>
        /// <param name="gy"></param>
        public static void GoogleTile(int tx, int ty, int zoom, out int gx, out int gy)
        {
            gx = tx;
            gy = (int)Math.Pow(2, zoom) - 1 - ty;
        }

        /// <summary>
        /// 从经纬度坐标字符串转换为经纬度数值
        /// </summary>
        /// <param name="geocode"></param>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        /// <returns></returns>
        public static bool GeocodeToLatlon(string geocode, out double lat, out double lon)
        {
            string[] temp = geocode.Split(',');
            if (temp.Length == 2 && double.TryParse(temp[0], out lat) && double.TryParse(temp[1], out lon))
            {
                return true;
            }
            else
            {
                lat = 0;
                lon = 0;
                return false;
            }

        }


        //def QuadTree(self, tx, ty, zoom ):
        //    "Converts TMS tile coordinates to Microsoft QuadTree"

        //    quadKey = ""
        //    ty = (2**zoom - 1) - ty
        //    for i in range(zoom, 0, -1):
        //        digit = 0
        //        mask = 1 << (i-1)
        //        if (tx & mask) != 0:
        //            digit += 1
        //        if (ty & mask) != 0:
        //            digit += 2
        //        quadKey += str(digit)

        //    return quadKey

    }//end class
}//end namespace
